using System;
using System.Collections.Generic;
using System.Linq;

namespace RPGOnline.Remoting.Entities
{
    public class GroupManager
    {
        private readonly SessionContext m_sessionContext;
        private readonly UserManager m_userManager;
        private readonly ChatManager m_chatManager;

        public ChatGroup DefaultChatGroup { get; set; }

        public event EventHandler<GroupEventArgs> OnGroupAdded;
		public event EventHandler<GroupEventArgs> OnGroupRemoved;
        public event EventHandler<UserGroupEventArgs> OnUserAddedToGroup;
		public event EventHandler<UserGroupEventArgs> OnUserRemovedFromGroup;
        public event EventHandler<UserGroupEventArgs> OnUserInfoChanged;

        public GroupManager(SessionContext sessionContext, UserManager userManager, ChatManager chatManager)
        {
            m_sessionContext = sessionContext;
            m_userManager = userManager;
            m_chatManager = chatManager;
            m_chatManager.OnUserRegistered += (s, e) => AddUser(e.User);
            m_chatManager.OnUserUnregistered += (s, e) => RemoveUser(e.User);
            m_chatManager.OnUserInfoChanged += (s, e) => UpdateUserInfo(e.User);
            m_chatManager.OnGroupAdded += (s, e) => AddGroup(e.ChatGroup);
            m_chatManager.OnUserMovedToGroup += (s, e) => MoveUserToGroup(e.User, e.GroupId);

            DefaultChatGroup = m_sessionContext.MainChatGroup;
        }

        private void UpdateUserInfo(User user)
        {
            ChatGroup chatGroup = FindChatGroupByUser(user);
            if (chatGroup == null) return;
            chatGroup.Users[user.Id] = user;

            if (OnUserInfoChanged != null)
                OnUserInfoChanged(this, new UserGroupEventArgs(user, chatGroup));
        }

        public static ChatGroup CreateGroup(string groupName)
        {
            ChatGroup chatGroup = new ChatGroup(groupName, Guid.NewGuid());
            return chatGroup;
        }

        private void AddGroup(ChatGroup chatGroup)
        {
            m_sessionContext.AddGroup(chatGroup);

            if (OnGroupAdded != null)
                OnGroupAdded(this, new GroupEventArgs(chatGroup));
        }

        private void RemoveGroup(ChatGroup chatGroup)
        {
            foreach (User user in chatGroup.Users.Values)
            {
                MoveUserToGroup(user, Guid.Empty);
            }
            m_sessionContext.RemoveGroup(chatGroup.Id);

            if (OnGroupRemoved != null)
				OnGroupRemoved(this, new GroupEventArgs(chatGroup));
        }

        public ChatGroup FindChatGroupByUser(User user)
        {
            return m_sessionContext.Groups.FirstOrDefault(chatGroup => chatGroup.Contains(user.Id));
        }

        private void AddUser(User user)
        {
            m_sessionContext.AddUser(user, m_sessionContext.MainChatGroup);

            if (user.Equals(m_userManager.CurrentUser))
                m_userManager.CurrentUserGroup = m_sessionContext.MainChatGroup;

            if (OnUserAddedToGroup != null)
                OnUserAddedToGroup(this, new UserGroupEventArgs(user, m_sessionContext.MainChatGroup));
        }

        private void AddUser(User user, ChatGroup chatGroup)
        {
            m_sessionContext.AddUser(user, chatGroup);

            if (OnUserAddedToGroup != null)
                OnUserAddedToGroup(this, new UserGroupEventArgs(user, chatGroup));
        }

        private bool RemoveUser(User user)
        {
            ChatGroup chatGroup = FindChatGroupByUser(user);
            if (chatGroup == null)
                return false;

            chatGroup.Remove(user);

            if (OnUserRemovedFromGroup != null)
                OnUserRemovedFromGroup(this, new UserGroupEventArgs(user, chatGroup));

            return true;
        }

        private bool MoveUserToGroup(User user, Guid groupId)
        {
            ChatGroup destinationGroup = m_sessionContext.FindGroupById(groupId);
            if (destinationGroup == null)
                return false;

            if (user.Equals(m_userManager.CurrentUser))
                m_userManager.CurrentUserGroup = destinationGroup;

            RemoveUser(user);
            AddUser(user, destinationGroup);
            return true;
        }
    }

	public class UserGroupEventArgs : EventArgs
	{
		public User User { get; set; }
		public ChatGroup Group { get; private set; }

		public UserGroupEventArgs(User user, ChatGroup group)
		{
			User = user;
			Group = @group;
		}
	}

	public class GroupEventArgs : EventArgs
	{
		public ChatGroup Group { get; private set; }

		public GroupEventArgs(ChatGroup group)
		{
			Group = @group;
		}
	}
}